package com.bfb;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.location.Geocoder;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.bfb.helpers.ServerUtilities;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

public class RestaurantMap extends MapActivity {
	static String PATH1 = "/location";
	MapOverlay itemizedOverlay;
	List<Overlay> mapOverlays;
	Drawable drawable;
	MapView mapView;
	GeoPoint p = null;
	private static URL myURL;
	private static URLConnection uc;
	private static InputStream in;
	String area = "";
	JSONObject query;
	JSONArray results;
	String restaurant_id = "";
	String latitude = "";
	String longitude = "";
	String not_found = "";

	/** Called when the activity is first created. */
	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/***
	 * Searches for a place or a location in the db & displays the returned
	 * locations on the map
	 * 
	 * @param area
	 *            initial text in the search text fields
	 */
	public void changeMap(String area) {
		mapView.getOverlays().clear();
		mapView.invalidate();
		MapController mc = mapView.getController();
		GeoPoint myLocation = null;
		double lat = 0;
		double lng = 0;
		try {

			if (area.length() > 0) {
				String[] txt = area.split(" ");
				area = txt[0];
				for (int i = 1; i < txt.length; i++)
					area = area + "+" + txt[i];

			}
			System.out.println(area);
			query = getJSONObject("https://maps.googleapis.com/maps/api/place/search/json?location=30.044244,31.237135&radius=50000&name="
					+ area
					+ "&sensor=false&key=AIzaSyD0Xk46UiDmamo3TOkcB3iMdXZnZXPHvTs");

			results = new JSONArray();
			JSONObject jsonLocation;
			try {
				results = query.getJSONArray("results");
				System.out.println("results = " + results.length());
				for (int i = 0; i < results.length(); i++) {
					jsonLocation = results.getJSONObject(i)
							.getJSONObject("geometry")
							.getJSONObject("location");
					lat = Double.parseDouble(jsonLocation.getString("lat"));
					lng = Double.parseDouble(jsonLocation.getString("lng"));
					p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
					OverlayItem overlayitem = new OverlayItem(p, "", "");
					itemizedOverlay.addOverlay(overlayitem);
				}
				in = null;
				mapOverlays.add(itemizedOverlay);
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			System.out.println("lat = " + lat);
			System.out.println("lng = " + lng);

			if (results.length() == 0) {
				Geocoder g = new Geocoder(this, Locale.getDefault());

				java.util.List<android.location.Address> r = g
						.getFromLocationName(area, 1);
				if (r.size() > 0) {

					Toast.makeText(
							RestaurantMap.this,
							"country: "
									+ String.valueOf(r.get(0).getCountryName()),
							Toast.LENGTH_SHORT).show();
					lat = r.get(0).getLatitude();
					lng = r.get(0).getLongitude();
				} else {
					Toast.makeText(RestaurantMap.this, "record not found",
							Toast.LENGTH_SHORT).show();
					return;
				}
			}
		} catch (IOException io) {
			Toast.makeText(RestaurantMap.this, "Connection Error",
					Toast.LENGTH_SHORT).show();
		}
		myLocation = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
		mc.animateTo(myLocation);
		mc.setZoom(10);

		mapView.invalidate();
		if (!restaurant_id.equals(""))
			getDishLocationsDB(restaurant_id);
	}

	/***
	 * Retrieves restaurant locations (latitude & longitude) from the database &
	 * saves them in latitude & longitude strings
	 * 
	 * @param id
	 *            Restaurant id where the dish is located
	 */
	public void getDishLocationsDB(String id) {
		Log.e("getloc", "in get dish locations");
		try {
			DefaultHttpClient CLIENT = new DefaultHttpClient();
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
			nameValuePairs.add(new BasicNameValuePair("id", id));
			URI url = URIUtils.createURI("http", Main.SERVER, -1, PATH1,
					URLEncodedUtils.format(nameValuePairs, "UTF-8"), null);
			HttpGet httpget = new HttpGet(url);
			HttpResponse resp = CLIENT.execute(httpget);
			StatusLine statusLine = resp.getStatusLine();
			int statusCode = statusLine.getStatusCode();
			Log.e("statuscode", statusCode + "");

			final StringBuilder respStr = new StringBuilder();
			HttpEntity entity = resp.getEntity();
			InputStream content = entity.getContent();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					content));
			String line;
			while ((line = reader.readLine()) != null) {
				respStr.append(line);
			}
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					try {
						// read returned json objects
						System.out.println("respStr = " + respStr);
						JSONArray jsonArray = new JSONArray(respStr.toString());
						for (int i = 0; i < ((JSONArray) jsonArray.get(0))
								.length(); i++) {
							JSONObject jsonObject = ((JSONArray) jsonArray
									.get(0)).getJSONObject(i);
							JSONArray j_coords = jsonObject
									.getJSONArray("latitude");

							for (int j = 0; j < j_coords.length(); j++)
								latitude += j_coords.getString(j) + "#";
						}
						for (int i = 0; i < ((JSONArray) jsonArray.get(1))
								.length(); i++) {
							JSONObject jsonObject = ((JSONArray) jsonArray
									.get(1)).getJSONObject(i);
							JSONArray j_coords = jsonObject
									.getJSONArray("longitude");

							for (int j = 0; j < j_coords.length(); j++)
								longitude += j_coords.getString(j) + "#";
						}
						System.out.println("latitude =" + latitude);
						System.out.println("longitude =" + longitude);

						MapController mc = mapView.getController();
						System.out.println("latt = " + latitude);
						System.out.println("lon = " + longitude);
						if (!latitude.equals("") && !longitude.equals("")) {
							String[] latt = latitude.split("#");
							String[] lon = longitude.split("#");
							if (latt.length == 0)
								Toast.makeText(getBaseContext(), not_found,
										Toast.LENGTH_LONG).show();
							System.out.println("latt.length = " + latt.length);
							System.out.println("lon.length = " + lon.length);
							for (int i = 0; i < latt.length; i++) {
								System.out.println("latt[i] = " + latt[i]);
								System.out.println("lon[i] = " + lon[i]);
								p = new GeoPoint(
										(int) (Double.parseDouble(latt[i]) * 1E6),
										(int) ((Double.parseDouble(lon[i])) * 1E6));
								OverlayItem overlayitem = new OverlayItem(p,
										"", "");
								itemizedOverlay.addOverlay(overlayitem);
							}
							mc.animateTo(p);
							mapOverlays.add(itemizedOverlay);
							mc.setZoom(10);
						}

					} catch (Exception e) {
						runOnUiThread(new Runnable() {
							@Override
							public void run() {
								Toast.makeText(getBaseContext(), "ERROR",
										Toast.LENGTH_LONG).show();
							}
						});
					}
				}
			});
		} catch (Exception e) {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					Toast.makeText(getBaseContext(), "ERROR", Toast.LENGTH_LONG)
							.show();
				}
			});
		}

	}

	/***
	 * Called when the activity is first started
	 * 
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		System.out.print("in map view");
		super.onCreate(savedInstanceState);
		not_found = this.getResources().getString(R.string.address_not_found);
		setContentView(R.layout.mapview);
		mapView = (MapView) findViewById(R.id.mapview);
		mapView.setBuiltInZoomControls(true);
		mapOverlays = mapView.getOverlays();
		drawable = this.getResources().getDrawable(R.drawable.google_pin);
		itemizedOverlay = new MapOverlay(drawable);
		
		System.out.print("in map view");
		Button btnSearch = (Button) findViewById(R.id.btnSearch);

		Bundle extras = getIntent().getExtras();
		area = extras.getString("rest_name");
		restaurant_id = extras.getString("rest_id");
		if (area.length() > 0) {
			EditText txtSearch = (EditText) findViewById(R.id.txtMapSearch);
			txtSearch.append(area);
			RestaurantMap.this.changeMap(area);
		}
		if(!restaurant_id.equals(""))
			itemizedOverlay.tap = true;
		btnSearch.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {

				EditText txtSearch = (EditText) findViewById(R.id.txtMapSearch);
				area = txtSearch.getText().toString();
				// Toast.makeText(GoogleMap.this, "Click-" +
				// String.valueOf(area), Toast.LENGTH_SHORT).show();
				RestaurantMap.this.changeMap(area);

			}
		});

		mapView = (MapView) findViewById(R.id.mapview);
		mapView.displayZoomControls(true);

		mapView.invalidate();
	}

	/***
	 * Creates buttons that will be displayed in the menu when this activity is
	 * created
	 * 
	 * @param menu
	 *            menu associated with this activity
	 * 
	 * @return return true when the menu buttons are created
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.address_menu, menu);
		return true;
	}

	/***
	 * Handles selecting an item from the menu
	 * 
	 * @param item
	 *            selected menu item
	 * 
	 * @return returns true after doing the action associated with selecting the
	 *         item
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		if (item.getItemId() == R.id.add_address_menuItem) {
			itemizedOverlay.tap = true;

		}
		if (item.getItemId() == R.id.save_address) {
			if (restaurant_id.equals(""))
				returnResult();
			else {
				editRestLoc(area, itemizedOverlay.latitudes,
						itemizedOverlay.longitudes);
				Intent intent = new Intent(RestaurantMap.this,
						MapViewActivity.class);
				intent.putExtra("loca_key", restaurant_id);
				intent.putExtra("loca_name", area);
				setResult(RESULT_OK, intent);
				finish();
			}
		}
		return super.onOptionsItemSelected(item);
	}

	/***
	 * Adds new locations in the db to a certain restaurant
	 * 
	 * @param restaurantName
	 *            name of a restaurant saved in the db
	 * @param longitudes
	 *            list of doubles that represent the new longitudes
	 * @param latitudes
	 *            list of doubles that represent the new latitudes
	 */
	public static void editRestLoc(final String restaurantName,
			final ArrayList<Double> longitudes,
			final ArrayList<Double> latitudes) {
		System.out.println(restaurantName + "----" + longitudes.toString()
				+ "---" + latitudes.toString());
		ServerUtilities.background(new Runnable() {
			@Override
			public void run() {
				try {
					DefaultHttpClient httpClient = new DefaultHttpClient();
					HttpPost httppost = new HttpPost("http://" + Main.SERVER
							+ "/edit");

					String longitudesString = "";
					String latitudesString = "";
					if (longitudes != null) {
						int longitudesLength = longitudes.size() - 1;
						for (int i = 0; i < longitudesLength; i++) {
							longitudesString += longitudes.get(i) + "#";
							latitudesString += latitudes.get(i) + "#";
						}
						longitudesString += longitudes.get(longitudesLength);
						latitudesString += latitudes.get(longitudesLength);
					}

					ArrayList<NameValuePair> nvpList = new ArrayList<NameValuePair>();
					nvpList.add(new BasicNameValuePair("restaurantName",
							restaurantName));
					nvpList.add(new BasicNameValuePair("longitudes",
							longitudesString));
					nvpList.add(new BasicNameValuePair("latitudes",
							latitudesString));

					System.out.println("llll " + latitudesString);
					System.out.println("0000 " + longitudesString);
					httppost.setEntity(new UrlEncodedFormEntity(nvpList));

					HttpResponse response = httpClient.execute(httppost);

					if (response == null) {
						throw new Exception("Response NULL");
					}
				} catch (Exception e) {
					Log.e("Post new dish", e.getMessage(), e);
				}
			}
		});
	}// end of editRestLoc

	/***
	 * Establishes a connection & sends a post request to get locations of a
	 * certain place from google places api
	 * 
	 * @param Url
	 *            url sent to get addresses from google maps
	 * @return return response in the form of json object
	 */
	public static JSONObject getJSONObject(String Url) {
		String res = null;
		JSONObject producedObject = null;
		initURL(Url);
		connect();
		try {
			res = convertStreamToString(in);
			System.out.print(res);
			producedObject = new JSONObject(res);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return producedObject;
	}// end of getJSONObject

	/***
	 * 
	 * @param url
	 *            url used to get addresses from google places api
	 */
	private static void initURL(String url) {
		try {
			myURL = new URL(url);
		} catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}// end of initURL

	/***
	 * Opens a connection & sends the request to get the locations from google
	 * api
	 * 
	 */
	private static void connect() {
		try {
			int retry = 0;
			while (retry < 3 && in == null) {
				uc = myURL.openConnection();
				uc.setReadTimeout(10000);
				uc.setConnectTimeout(10000);
				in = uc.getInputStream();
				retry++;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}// end of connect

	/**
	 * To convert the InputStream to String we use the BufferedReader.readLine()
	 * method. We iterate until the BufferedReader return null which means
	 * there's no more data to read. Each line will appended to a StringBuilder
	 * and returned as String.
	 * 
	 * @param is
	 *            Inputstream
	 */
	private static String convertStreamToString(InputStream is)
			throws IOException {
		if (is != null) {
			StringBuilder sb = new StringBuilder();
			String line;
			try {
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(is, "ISO_8859_1"), 16192);
				while ((line = reader.readLine()) != null) {
					sb.append(line).append("\n");
				}
			} finally {
				is.close();
			}
			return sb.toString();
		} else {
			return "";
		}
	}// end of convertStreamToString

	@Override
	public void onBackPressed() {
		returnResult();
	}

	private void returnResult() {
		itemizedOverlay.tap = false;
		System.out.println(itemizedOverlay.latitudes.toString());
		System.out.println(itemizedOverlay.longitudes.toString());
		Intent intent = new Intent();
		TextView searchField = (TextView) findViewById(R.id.txtMapSearch);
		intent.putExtra("restaurantName", searchField.getText().toString());
		double[][] longsLats = prepLongsAndLats(itemizedOverlay.longitudes,
				itemizedOverlay.latitudes);
		intent.putExtra("latitudes", longsLats[0]);
		intent.putExtra("longitudes", longsLats[1]);
		setResult(RESULT_OK, intent);
		finish();
	}

	private double[][] prepLongsAndLats(ArrayList<Double> longitudes,
			ArrayList<Double> latitudes) {
		int size = longitudes.size();
		double[][] longsLats = new double[2][size];
		for (int i = 0; i < size; i++) {
			longsLats[0][i] = longitudes.get(i).doubleValue();
			longsLats[1][i] = latitudes.get(i).doubleValue();
		}
		return longsLats;
	}
}